/**
 * QiniuDownloadService.java
 * com.sftj.framework.service.qiniu.config
 * Copyright (c) 2017, 北京科技有限公司版权所有.
*/

package com.sftz.framework.service.qiniu;

import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Set;
import java.util.UUID;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ConnectionPoolTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Encoding;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import com.google.common.collect.Sets;
import com.google.common.io.ByteStreams;
import com.qiniu.common.QiniuException;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import com.sftz.framework.service.BaseUploadService;
import com.sftz.framework.service.qiniu.config.QiniuAkConfig;
import com.sftz.framework.util.DownloadUtil;
import com.sftz.framework.util.HttpClientUtil;
import com.uxuexi.core.common.exception.impl.BusinessException;
import com.uxuexi.core.common.util.Util;

/**
 * 七牛下载服务
 * @author   朱晓川
 * @Date	 2017年12月13日 	 
 */
@IocBean(name = "qiniuDownloadService")
public class QiniuDownloadService extends BaseUploadService {

	private static final Log log = Logs.get();

	private static final String IMAGE_BUCKET = "image-open-plat";
	private static final String FILE_BUCKET = "file-open-plat";

	private Auth auth = Auth.create(QiniuAkConfig.ACCESS_KEY, QiniuAkConfig.SECRET_KEY);

	/**
	 * 默认过期时间5分钟
	 */
	private static final long DEFAULT_EXPIRES = 10000;

	//服务器允许暴露的响应头
	private static final String EXPOSE_HEADER_NAME = "Access-Control-Expose-Headers";

	//服务器支持RANGE REQUEST的响应头
	private static final String CONTENT_LENGTH = "Content-Length";

	//range 请求头
	private static final String REQ_HEADER_RANGE = "range";
	private static final String REQ_HEADER_HOST = "host";
	//文件服务器(host 请求头)
	private static final String HOST_HEADER_FILE = "file.sktcm.cn";

	// 重用 uploadManager。一般地，只需要创建一个 uploadManager 对象
	private static UploadManager uploadManager;

	static {
		Zone zone = Zone.zone1();
		Configuration conf = new Configuration(zone);
		uploadManager = new UploadManager(conf);
	}

	// 简单图片上传，使用默认策略
	private String getImageUpToken() {
		return auth.uploadToken(IMAGE_BUCKET);
	}

	// 简单文件上传
	public String getFileUpToken() {
		return auth.uploadToken(FILE_BUCKET);
	}

	//64kb缓冲区
	private static final int BUFFER_SIZE = 65536;

	public void proxyView(String key, HttpServletRequest request, HttpServletResponse response) {
		//请求地址
		String originUrl = key;
		String downUrl = getDownloadUrl(originUrl);
		HttpUriRequest httpGet = new HttpGet(downUrl);

		//设置请求头request headers
		Set<String> reqHeaders = Sets.newHashSet();
		Enumeration<String> headerNames = request.getHeaderNames();
		if (!Util.isEmpty(headerNames)) {
			while (headerNames.hasMoreElements()) {
				String name = headerNames.nextElement();
				reqHeaders.add(name);

				if (REQ_HEADER_HOST.equals(name)) {
					httpGet.addHeader(REQ_HEADER_HOST, HOST_HEADER_FILE);
				} else if (REQ_HEADER_RANGE.equals(name)) {
					//debug request range info
					log.debug("request--" + REQ_HEADER_RANGE + ":" + request.getHeader(name));
					httpGet.addHeader(name, request.getHeader(name));
				} else {
					httpGet.addHeader(name, request.getHeader(name));
				}
			}
		}

		CloseableHttpClient httpClient = HttpClientUtil.getHttpClient();
		InputStream ins = null;
		ServletOutputStream out = null;
		try {
			HttpResponse res = httpClient.execute(httpGet);
			int statusCode = res.getStatusLine().getStatusCode();
			log.debug("statusCode:" + statusCode);
			// 设置状态码，range请求的时候状态码为206,这里必须设置，否则range传输可能失效
			response.setStatus(statusCode);

			Set<String> resHeaders = Sets.newHashSet();

			//设置响应头信息response headers
			Header[] allHeaders = res.getAllHeaders();
			if (!Util.isEmpty(allHeaders)) {
				for (Header h : allHeaders) {
					String hName = h.getName();
					resHeaders.add(hName);
					if (EXPOSE_HEADER_NAME.equals(hName)) {
						//设置允许暴露的头信息
						response.addHeader(EXPOSE_HEADER_NAME, "*");
					} else if (CONTENT_LENGTH.equals(hName)) {
						//debug range 信息
						log.debug(CONTENT_LENGTH + ":" + h.getValue());
						response.addHeader(CONTENT_LENGTH, h.getValue());
					} else {
						response.addHeader(h.getName(), h.getValue());
					}
				}
			}

			ins = res.getEntity().getContent();
			out = response.getOutputStream();

			//缓冲区
			byte buffer[] = new byte[BUFFER_SIZE];
			int len = 0;
			while ((len = ins.read(buffer)) != -1) {
				out.write(buffer, 0, len);
			}
			//range 传输的时候，第一次获取到content length 后客户端会断开连接，tomcat会报客户端断开的异常
		} catch (ConnectionPoolTimeoutException e) {
			log.error("http get throw ConnectionPoolTimeoutException(wait time out)");
		} catch (ConnectTimeoutException e) {
			log.error("http get throw ConnectTimeoutException");
		} catch (SocketTimeoutException e) {
			log.error("http get throw SocketTimeoutException");
		} catch (Exception e) {
			log.warn(e.getMessage());
		} finally {
			try {
				if (null != ins) {
					ins.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			if (null != httpGet && !httpGet.isAborted()) {
				httpGet.abort();
			}
		}
	}

	/**
	 * 获取文件下载路径，即：donwloadUrl，手动设置链接过期时间
	 * @return
	 */
	public String getDownloadUrl(String targetUrl, long expires) {
		String downloadUrl = auth.privateDownloadUrl(targetUrl, expires);
		return downloadUrl;
	}

	/**
	 * 获取文件真实下载地址，使用默认过期时间5分钟
	 * <p>
	 * @param targetUrl 私有文件原始地址
	 */
	public String getDownloadUrl(String targetUrl) {
		return getDownloadUrl(targetUrl, DEFAULT_EXPIRES);
	}

	/**
	 * 下载七牛云私有空间文件,手动指定文件名
	 * <p>
	 *
	 * @param targetUrl
	 * @param fileName 
	 */
	public InputStream downloadPrivate(final HttpServletResponse response, String targetUrl, String fileName) {
		try {
			String filename = URLEncoder.encode(fileName, Encoding.UTF8);
			response.setHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
			String downloadUrl = getDownloadUrl(targetUrl);
			InputStream is = DownloadUtil.download(downloadUrl);
			return is;
		} catch (Exception e) {
			throw new BusinessException("下载失败！");
		}
	}

	public InputStream previewPrivate(final HttpServletResponse response, String targetUrl) {
		try {
			String filename = UUID.randomUUID().toString() + ".pdf";
			response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");

			String downloadUrl = getDownloadUrl(targetUrl);
			InputStream is = DownloadUtil.download(downloadUrl);
			return is;
		} catch (Exception e) {
			throw new BusinessException("预览失败！");
		}
	}

	/**
	 * 七牛图片上传，上传到图片bucket
	 */
	@Override
	public String uploadImage(InputStream inStream, String file_ext_name, String dest_filename) {

		byte[] data = null;
		try {
			data = ByteStreams.toByteArray(inStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
		String upToken = getImageUpToken();
		String fileName = UUID.randomUUID().toString() + "." + file_ext_name;

		DefaultPutRet ret = upload(data, fileName, upToken);
		return ret.key;
	}

	private DefaultPutRet upload(byte[] data, String key, String upToken) {
		DefaultPutRet ret = null;
		try {
			//华北区
			Response res = uploadManager.put(data, key, upToken);

			if (res.isOK()) {
				ret = res.jsonToObject(DefaultPutRet.class);
				return ret;
			} else {
				// 上传失败
				log.info(res.bodyString());
			}
		} catch (QiniuException e) {
			Response r = e.response;
			// 请求失败时简单状态信息
			log.error(r.toString());
			try {
				// 响应的文本信息
				log.error(r.bodyString());
			} catch (QiniuException e1) {
				// ignore
			}
		}
		return ret;
	}

	/**
	 * 七牛文件上传，上传到文件bucket
	 */
	@Override
	public String uploadFile(InputStream inStream, String file_ext_name, String dest_filename) {

		byte[] data = null;
		try {
			data = ByteStreams.toByteArray(inStream);
		} catch (IOException e) {
			e.printStackTrace();
		}
		String upToken = getFileUpToken();//file
		String fileName = UUID.randomUUID().toString() + "." + file_ext_name;

		DefaultPutRet ret = upload(data, fileName, upToken);
		return ret.key;

	}

}
